using System;
using System.Collections;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Messaging;

using Pegasus.Diagnostics;
using Pegasus.Runtime.Remoting.Messaging;
using Pegasus.Runtime.Serialization.Formatters.Xml;
using Pegasus.Log4Net;

namespace Pegasus.Runtime.Remoting.Channels
{
    /// <summary>
    /// Provides the implementation for a server formatter sink that uses the XmlFormatter2 class. 
    /// </summary>
    public class XmlServerFormatterSink : IServerChannelSink
    {
        private static ILog s_logger = LogManager.GetLogger( typeof( XmlClientFormatterSink ) );

        // Local Instance Values
        private IServerChannelSink m_nextChannelSink;

        /// <summary>
        /// Initializes a new instance of the <see cref="XmlServerFormatterSink"/> class.
        /// </summary>
        /// <param name="nextChannelSink">The next channel sink.</param>
        public XmlServerFormatterSink( IServerChannelSink nextChannelSink )
        {
            // Check Parameters
            ParamCode.AssertNotNull( nextChannelSink, "nextChannelSink" );

            m_nextChannelSink = nextChannelSink;
        }

        /// <summary>
        /// Gets a dictionary through which properties on the sink can be accessed.
        /// </summary>
        /// <value></value>
        /// <returns>A dictionary through which properties on the sink can be accessed, or null if the channel sink does not support properties.</returns>
        /// <exception cref="T:System.Security.SecurityException">The immediate caller does not have infrastructure permission. </exception>
        /// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
        public IDictionary Properties
        {
            get
            {
                return null;
            }
        }

        /// <summary>
        /// Gets the next server channel sink in the server sink chain.
        /// </summary>
        /// <value></value>
        /// <returns>The next server channel sink in the server sink chain.</returns>
        /// <exception cref="T:System.Security.SecurityException">The immediate caller does not have the required <see cref="F:System.Security.Permissions.SecurityPermissionFlag.Infrastructure"></see> permission. </exception>
        /// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="Infrastructure"/></PermissionSet>
        public IServerChannelSink NextChannelSink
        {
            get
            {
                return m_nextChannelSink;
            }
        }

        /// <summary>
        /// Returns the <see cref="T:System.IO.Stream"></see> onto which the provided response message is to be serialized.
        /// </summary>
        /// <param name="sinkStack">A stack of sinks leading back to the server transport sink.</param>
        /// <param name="state">The state that has been pushed to the stack by this sink.</param>
        /// <param name="msg">The response message to serialize.</param>
        /// <param name="headers">The headers to put in the response stream to the client.</param>
        /// <returns>
        /// The <see cref="T:System.IO.Stream"></see> onto which the provided response message is to be serialized.
        /// </returns>
        /// <exception cref="T:System.Security.SecurityException">The immediate caller does not have infrastructure permission. </exception>
        public Stream GetResponseStream( IServerResponseChannelSinkStack sinkStack, object state, IMessage msg, ITransportHeaders headers )
        {
            throw new NotSupportedException( "GetResponseStream method is not supported" );
        }


        /// <summary>
        /// Requests processing from the current sink of the response from a method call sent asynchronously.
        /// </summary>
        /// <param name="sinkStack">A stack of sinks leading back to the server transport sink.</param>
        /// <param name="state">Information generated on the request side that is associated with this sink.</param>
        /// <param name="msg">The response message.</param>
        /// <param name="headers">The headers to add to the return message heading to the client.</param>
        /// <param name="stream">The stream heading back to the transport sink.</param>
        /// <exception cref="T:System.Security.SecurityException">The immediate caller does not have infrastructure permission. </exception>
        public void AsyncProcessResponse( IServerResponseChannelSinkStack sinkStack, object state, IMessage msg, ITransportHeaders headers, Stream stream )
        {
            throw new NotImplementedException( "AsyncProcessResponse method has not been implemented" );
        }

        /// <summary>
        /// Requests message processing from the current sink.
        /// </summary>
        /// <param name="sinkStack">A stack of channel sinks that called the current sink.</param>
        /// <param name="requestMsg">The message that contains the request.</param>
        /// <param name="requestHeaders">Headers retrieved from the incoming message from the client.</param>
        /// <param name="requestStream">The stream that needs to be to processed and passed on to the deserialization sink.</param>
        /// <param name="responseMsg">When this method returns, contains a <see cref="T:System.Runtime.Remoting.Messaging.IMessage"></see> that holds the response message. This parameter is passed uninitialized.</param>
        /// <param name="responseHeaders">When this method returns, contains a <see cref="T:System.Runtime.Remoting.Channels.ITransportHeaders"></see> that holds the headers that are to be added to return message heading to the client. This parameter is passed uninitialized.</param>
        /// <param name="responseStream">When this method returns, contains a <see cref="T:System.IO.Stream"></see> that is heading back to the transport sink. This parameter is passed uninitialized.</param>
        /// <returns>
        /// A <see cref="T:System.Runtime.Remoting.Channels.ServerProcessing"></see> status value that provides information about how message was processed.
        /// </returns>
        /// <exception cref="T:System.Security.SecurityException">The immediate caller does not have infrastructure permission. </exception>
        public ServerProcessing ProcessMessage( IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders,
            Stream requestStream, out IMessage responseMsg, out ITransportHeaders responseHeaders, out Stream responseStream )
        {
            // Check Parameters
            ParamCode.AssertNotNull( sinkStack, "sinkStack" );
            // requestMsg can be null
            ParamCode.AssertNotNull( requestHeaders, "requestHeaders" );
            ParamCode.AssertNotNull( requestStream, "requestStream" );
            // responseMsg is out param
            // responseHeaders is out param
            // responseStream is out param

            // Initialize the out parameters
            responseHeaders = null;
            responseMsg = null;
            responseStream = null;

            // Deserialze the incomming message
            XmlFormatter2 formatter = new XmlFormatter2( new PegasusRemotingSurrogateSelector(), new StreamingContext( StreamingContextStates.Other ) );
            PegasusMethodCall pegasusMethodCall = (PegasusMethodCall) formatter.Deserialize( requestStream );

            // Reset the message URI property. Found an article on the internet that explains why
            // we need to do this and to use our own method call object.
            // http://72.14.253.104/search?q=cache:otpcIHcD5YEJ:www.rz.uni-karlsruhe.de/~ry52/buch/14_kapitel8.pdf+IServerChannelSink+ProcessMessage&hl=en&gl=us&ct=clnk&cd=27
            string uri = (string) pegasusMethodCall.Properties[ "__Uri" ];
            int n = uri.LastIndexOf( "/" );
            if( n != -1 )
            {
                uri = uri.Substring( n );
                pegasusMethodCall.Properties[ "__Uri" ] = uri;
            }

            // Switch back to the system method call object
            MethodCall methodCall = new MethodCall( pegasusMethodCall );

            ServerProcessing ret = m_nextChannelSink.ProcessMessage( sinkStack, methodCall, requestHeaders, null,
                out responseMsg, out responseHeaders, out responseStream );

            if( ret == ServerProcessing.Complete )
            {
                // Serialize response message to the response stream.
                if( responseMsg != null && responseStream == null )
                {
                    responseStream = sinkStack.GetResponseStream( responseMsg, responseHeaders );
                    if( responseStream == null )
                    {
                        responseStream = new MemoryStream();
                    }

                    PegasusMethodCall pegasusResponseMethodCall = new PegasusMethodCall( responseMsg );
                    responseMsg = pegasusResponseMethodCall;

                    formatter.Serialize( responseStream, pegasusResponseMethodCall );

                    if( responseHeaders == null )
                    {
                        responseHeaders = new TransportHeaders();
                    }
                }
            }

            return ret;
        }
    }
}

